package hydrap2p.search;

import hydrap2p.ServiceLocation;
import hydrap2p.helper.ServiceConnector;
import hydrap2p.helper.ThriftServerThread;
import hydrap2p.modulemanager.ModuleControl;
import hydrap2p.servicemanager.NameTaken;

import java.io.IOException;
import java.net.BindException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeMap;
import java.util.Map.Entry;

import org.apache.thrift.TException;

public class Server implements Searcher.Iface, ModuleControl.Iface {

	private final Listeners listeners;
	private final Searches searches;
	private final ThriftServerThread server;
	private final ThriftServerThread controlserver;
	
	private static Timer timer;
	private static final long pingerdelay = 10000;
	
	public static void main(String[] args)
	{
		try {
			new Server();
	
		} catch (BindException e) {
			System.err.println("Failed to bind to a port");
		} catch (IOException e) {
			System.err.println("Failed to contact Manager");
		} catch (NameTaken e) {
			System.err.println( "Searcher name taken! This means that another Searcher instance is running");
		}
	}
	
	public Server() throws BindException, IOException, NameTaken
	{
		listeners=new Listeners();
		searches=new Searches(listeners);

		controlserver = new ThriftServerThread(new ModuleControl.Processor(this), "SearcherCTL", 3503);
		controlserver.start();
		server = new ThriftServerThread(new Searcher.Processor(this), "Searcher", 3502);
		server.start();
		
		timer = new Timer();
		timer.schedule(new PingTimer(), pingerdelay, pingerdelay);
	}

	private class PingTimer extends TimerTask {
		@Override
		public void run() {
			try {
				ServiceConnector.getServiceManager().ping();
				return;
			} catch (Exception e) {
			}
			stop();
		}
	}
	
	@Override
	public int startSearch(SearchQuery arg0) throws TException {
		return searches.startSearch(arg0);
	}

	@Override
	public int startSearchSome(SearchQuery arg0, List<String> arg1)
			throws TException {
		return searches.startSearchSome(arg0, arg1);
	}

	@Override
	public boolean stopSearch(int arg0) throws TException {
		return searches.stopSearch(arg0);
	}

	@Override
	public boolean restartSearch(int arg0) throws TException {
		return searches.restartSearch(arg0);
	}

	@Override
	public boolean deleteSearch(int arg0) throws TException {
		return searches.deleteSearch(arg0);
	}

	@Override
	public boolean addSearchResult(int arg0, SearchResult arg1) throws TException {
		return searches.addSearchResult(arg0, arg1);
	}

	@Override
	public boolean searchFinished(int arg0, String arg1) throws TException {
		return searches.searchFinished(arg0, arg1);
	}

	@Override
	public Map<Integer, SearchQuery> getSearches() throws TException {
		Map<Integer, Search> temp= searches.getSearches();
		Map<Integer, SearchQuery> ret=new TreeMap<Integer, SearchQuery>();
		synchronized(searches)
		{
			for (Entry<Integer, Search> entry: temp.entrySet())
			{
				ret.put(entry.getKey(), entry.getValue().getSearchQuery());
			}
		}
		return ret;
	}

	@Override
	public List<String> getSearchActiveProviders(int arg0) throws TException {
		return searches.getSearchActiveProviders(arg0);
	}

	@Override
	public List<String> getSearchTotalProviders(int arg0) throws TException {
		return searches.getSearchTotalProviders(arg0);
	}

	@Override
	public SearchQuery getSearchQuery(int arg0) throws TException {
		return searches.getSearchQuery(arg0);
	}

	@Override
	public List<SearchResult> getSearchResults(int arg0) throws TException {
		return searches.getSearchResults(arg0);
	}

	@Override
	public long download(String arg0, String arg1, int arg2, int arg3)
			throws TException {
		return searches.download(arg0, arg1, arg2, arg3);
	}

	@Override
	public boolean registerSearchProvider(ServiceLocation arg0) throws TException {
		return searches.registerSearchProvider(new SearcherThrift(arg0));
	}

	@Override
	public List<String> listSearchProviders() throws TException {
		return searches.listSearchProviders();
	}

	@Override
	public boolean unregisterSearchProvider(ServiceLocation arg0)
			throws TException {
		return searches.unregisterSearchProvider(arg0);
	}

	@Override
	public boolean subscribeListener(ServiceLocation arg0) throws TException {
		ListenerThrift listener=new ListenerThrift(arg0);	// will exception if didn't work
		new Thread(new ListenerAllSyncerator(listener, searches)).start();		// send it the list of searches
		return listeners.subscribeListener(listener);
	}

	@Override
	public boolean unsubscribeListener(ServiceLocation arg0) throws TException {
		return listeners.unsubscribeListener(arg0);
	}

	@Override
	public boolean subscribeSearchListener(ServiceLocation arg0, int arg1)
			throws TException {
		ListenerThrift listener=new ListenerThrift(arg0);	// will exception if didn't work
		Search temp=searches.getSearch(arg1);
		if (temp!=null)
			new Thread(new ListenerSearchSyncerator(listener,temp)).start();
		return listeners.subscribeSearchListener(listener, arg1);
	}

	@Override
	public boolean unsubscribeSearchListener(ServiceLocation arg0, int arg1)
			throws TException {
		return listeners.unsubscribeSearchListener(arg0, arg1);
	}

	@Override
	public void ping() throws TException {
		
	}

	@Override
	public List<String> supportedInterfaces() throws TException {
		return Arrays.asList(new String[] {"Searcher", "ModuleControl", "Service"});
	}

	@Override
	public void stop() {
		server.shutdown();
		System.exit(0);
	}

}
